package main

import (
	"context"
	"fmt"
	"time"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	metricsv "k8s.io/metrics/pkg/client/clientset/versioned"
)

const (
	targetNamespace = "test"
	deploymentName  = "myapp"
	maxReplicas     = 3
	minReplicas     = 1
	cpuThreshold    = 80 // 80%
	checkInterval   = 3 * time.Second
)

func main() {
	// 初始化Kubernetes客户端
	config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
	if err != nil {
		panic(err.Error())
	}
	fmt.Printf("config is:%+v", config)
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}

	// 初始化Metrics客户端
	metricsClient, err := metricsv.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}
	//
	/*Clientset 类型：*kubernetes.Clientset
	  它是 Kubernetes 客户端库中的核心类型，用于与 Kubernetes API 交互。
	  通过 clientset，你可以访问 Kubernetes 集群中的资源（如 Pods、Deployments、Services 等），并对这些资源执行 CRUD 操作。
	      fmt.Printf("clientset is:%+v", clientset)
	  	fmt.Printf("metricsClient is:%+v", metricsClient) */
	for {
		// 检查并维护Deployment
		err = maintainDeployment(clientset, metricsClient)
		if err != nil {
			fmt.Printf("Error maintaining deployment: %v\n", err)
		}

		time.Sleep(checkInterval)
	}
}

func maintainDeployment(clientset *kubernetes.Clientset, metricsClient *metricsv.Clientset) error {
	// 获取Deployment
	deploy, err := clientset.AppsV1().Deployments(targetNamespace).Get(
		context.TODO(),
		deploymentName,
		metav1.GetOptions{},
	)

	if errors.IsNotFound(err) {
		// 创建Deployment
		return createDefaultDeployment(clientset)
	} else if err != nil {
		return err
	}

	// 获取Metrics数据
	podMetrics, err := metricsClient.MetricsV1beta1().PodMetricses(targetNamespace).List(
		context.TODO(),
		metav1.ListOptions{
			LabelSelector: fmt.Sprintf("app=%s", deploymentName),
		},
	)
	if err != nil {
		return err
	}

	// 计算CPU使用率
	totalUsage := 0.0
	for _, metric := range podMetrics.Items {
		for _, container := range metric.Containers {
			cpu := container.Usage[corev1.ResourceCPU]
			totalUsage += cpu.AsApproximateFloat64()
		}
	}

	currentReplicas := *deploy.Spec.Replicas
	averageUsage := totalUsage / float64(currentReplicas)

	// 获取请求的CPU限制
	// 获取请求的CPU限制
	requestedCPU := deploy.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu()
	calculatedThreshold := float64(requestedCPU.MilliValue()) * (float64(cpuThreshold) / 100) / 1000
	fmt.Printf("Current replicas: %d, Average usage: %.8f, CPU threshold: %.8f\n", currentReplicas, averageUsage, calculatedThreshold)

	// 扩缩容逻辑
	if averageUsage > calculatedThreshold && currentReplicas < maxReplicas {
		newReplicas := currentReplicas + 1
		fmt.Printf("Scaling up to %d replicas\n", newReplicas)
		return scaleDeployment(clientset, newReplicas)
	} else if averageUsage < (calculatedThreshold*0.5) && currentReplicas > minReplicas {
		fmt.Println("Scaling down...,waiting 60 seconds")
		time.Sleep(60 * time.Second) // 等待60秒
		newReplicas := currentReplicas - 1
		fmt.Printf("Scaling down to %d replicas\n", newReplicas)
		return scaleDeployment(clientset, newReplicas)
	}

	return nil
}

func createDefaultDeployment(clientset *kubernetes.Clientset) error {
	replicas := int32(minReplicas)
	_, err := clientset.AppsV1().Deployments(targetNamespace).Create(
		context.TODO(),
		&appsv1.Deployment{
			ObjectMeta: metav1.ObjectMeta{
				Name:      deploymentName,
				Namespace: targetNamespace,
			},
			Spec: appsv1.DeploymentSpec{
				Replicas: &replicas,
				Selector: &metav1.LabelSelector{
					MatchLabels: map[string]string{"app": deploymentName},
				},
				Template: corev1.PodTemplateSpec{
					ObjectMeta: metav1.ObjectMeta{
						Labels: map[string]string{"app": deploymentName},
					},
					Spec: corev1.PodSpec{
						Containers: []corev1.Container{
							{
								Name:  "main",
								Image: "wangjzhao/test:v3",
								Resources: corev1.ResourceRequirements{
									Requests: corev1.ResourceList{
										corev1.ResourceCPU: resource.MustParse("500m"),
									},
								},
							},
						},
					},
				},
			},
		},
		metav1.CreateOptions{},
	)
	return err
}

func scaleDeployment(clientset *kubernetes.Clientset, newReplicas int32) error {
	deploy, err := clientset.AppsV1().Deployments(targetNamespace).Get(
		context.TODO(),
		deploymentName,
		metav1.GetOptions{},
	)
	if err != nil {
		return err
	}

	deploy.Spec.Replicas = &newReplicas
	_, err = clientset.AppsV1().Deployments(targetNamespace).Update(
		context.TODO(),
		deploy,
		metav1.UpdateOptions{},
	)
	return err
}
